Finding The Right Place To Edit
Suppose you want to change some mechanic of Diablo, but you don't know what part of the code encapsulates your mechanic. So you want to make educated guesses that allow you to find that place quickly. There are a few methods that are commonly used; Which method to use depends a lot on what information you already have. Using A Description Of What The Function Does If you know what the function does, you can look through our lists of functions and the dll pages and check if one of the function names or descriptions matches what you're trying to do. See if the description has a code address or ordinal attached to it for your Diablo version. For example, if you're modding 1.10f and looking for a function that assigns some property to an item. By looking at the functions, you see a function named ItemAssignProperty - and in fact it has the code locations and a list of parameters for 1.10f! Hallelujah! Of course it's not always that easy and more often than not the function you're searching will either not be in our database at all, or it will be there for other versions only. Since checking this is extremely easy and quick, it's the first thing you should do. If it fails, try any of the other strategies. Using A Concrete Value If you have a value of something related to what you want to change, search for the value in memory and in the code. As an example, consider the edit that makes all items take their gambling cost from the ItemsTXTi.GambleCost, not just rings and amulets. We expect that the code must somehow check if the item is a ring or amulet. If we're lucky, the code will explicitly mention the item codes of rings and/or amulets. We know that the string for ring is 'rin ' and that the string for amulet is 'amu '. Searching for all occurrences of that string in the memory (in OllyDbg, use CTRL+B and search for the hexadecimal ASCII representation) gives you all the places where the string is loaded, compared to, added, substracted, etc. One of those places might (!) be the one that we need. In this example it will be, but we're not always this lucky. If you can not find the value in the code, it will probably be somewhere in memory. By finding all instances of the value in memory and proceeding as below, you have good chances of finding the code - but this is a very slow and tedious method. Just for some more examples of concrete values, consider string indices from TBL files, complete strings, item type codes, character class codes (for example for the locked chests), and many more. Using A Function That Might Be Used If you can think of a sort of functionality that might be used in the code you want to change, try to see if we know functions that encapsulate that behavior. As a simple example, we want to find the formula that calculates the chance to hit on attack. That function will need to get the attack rating and the defence rating of some units. Therefore we see if we have the location of functions that get the attack rating and the defence rating. If so, we can easily track all the places where the function is called (search for intermodular calls, or set a breakpoint at the function and look at the stack to see the return address). Again in our example we are lucky (at least in v1.10f) and the code of RollAttackHit calls GetUnitAttackRating. Sometimes, however, the code of the called function is inlined. It's not clear how to progress then. Using A Register That Contains The Data When you have found a register that contains the right information, you can track it (in OllyDbg, double click it) and see where it is accessed the next time. If the register is saved to memory, proceed as below. Using A Memory Location Where The Data Is Stored Many tools allow you to track down where a certain memory location is accessed, i.e., being read or written (in OllyDbg, use memory breakpoints). When a value that you think is used in the code you want to modify is being stored to memory, track all reads to that memory location and run the program until the next time that memory location is being read. Then proceed following the code as usually. Be careful not to step too far and check that the value at the point of reading is still the value that was written. Using Some Code That Comes Later Let's say you have access to some code that you know (or believe) is always executed after the code you want to change is executed. For example, you want to modify what a shrine does and you have found the place where the shrine message is displayed, and you believe that the shrine code actually precedes the code with the message. Then you might want to use time traveling features, but only few debuggers actually have such features (like UndoDB). Thus do what every good debugger does, and use the scientific method. Build up theories about the instruction trace, then test your theories. For example, follow back any conditional jumps. If you can, think about whether that condition would be true in your case. Add a break point to the jump and try to reproduce a run and see if the breakpoint is reached. Especially if it's ambiguous, add a break at every instruction that could be followed by the last instruction which you know to be part of the trace. You might end up with a huge tree of potential traces that will all result in the code you are looking at being executed. If you're lucky, your code is in one of the branches of that tree. But your initial assumption, that the code you are looking at always follows the other code, or at least follows it within a reasonable number of instructions, might just be wrong. In general, this might be the most tedious and less fruitful method of finding the exact place you want to modify. It makes sense to try other methods first and go to this method only if you absolutely must. Using Some Code From Another Version If you have the binary code from another version, you can often use it to find the same code in your own version. Make sure to generalize the registers, then search for a couple of instructions that you know are part of the other version's code. This doesn't always work. While often only the registers change, sometimes additional compiler optimizations change the structure of code, or the underlying code has actually been changed. In any of those cases, the instructions you're looking at probably won't match the instructions you're searching for at all. Category:CodeEditing